﻿namespace Lilium.CSharp.Expressions.Substitutions

open System.Runtime.CompilerServices

[<Extension>]
module Operators =
    
    open System.Linq.Expressions

    open Lilium.CSharp.Expressions.Evaluators
    open Lilium.CSharp.Expressions.Patterns
    open Lilium.CSharp.Expressions.Substitutions.Utils

    type private BinaryOperator =
        | Add                   = 0
        | AddChecked            = 1
        | Divide                = 2
        | Modulo                = 3
        | Multiply              = 4
        | MultiplyChecked       = 5
        | Power                 = 6
        | Subtract              = 7
        | SubtractChecked       = 8
        | And                   = 9
        | Or                    = 10
        | ExclusiveOr           = 11
        | LeftShift             = 12
        | RightShift            = 13
        | AndAlso               = 14
        | OrElse                = 15
        | Equal                 = 16
        | NotEqual              = 17
        | GreaterThan           = 18
        | GreaterThanOrEqual    = 19
        | LessThan              = 20
        | LessThanOrEqual       = 21
        | Coalesce              = 22
        | ArrayIndex            = 23

    type private BinaryOperatorSubstitution (op : BinaryOperator) =
        inherit SubstitutionAttribute ()

        override self.GetExpression transform meth args typeArgs =
            match args, typeArgs with
            | leftExpr' :: rightExpr' :: [], [] 
                ->  let leftExpr = transform leftExpr'
                    let rightExpr = transform rightExpr'
                    match op with
                    | BinaryOperator.Add                   -> Expression.Add (leftExpr, rightExpr) 
                    | BinaryOperator.AddChecked            -> Expression.AddChecked (leftExpr, rightExpr) 
                    | BinaryOperator.Divide                -> Expression.Divide (leftExpr, rightExpr) 
                    | BinaryOperator.Modulo                -> Expression.Modulo (leftExpr, rightExpr) 
                    | BinaryOperator.Multiply              -> Expression.Multiply (leftExpr, rightExpr) 
                    | BinaryOperator.MultiplyChecked       -> Expression.MultiplyChecked (leftExpr, rightExpr) 
                    | BinaryOperator.Power                 -> Expression.Power (leftExpr, rightExpr) 
                    | BinaryOperator.Subtract              -> Expression.Subtract (leftExpr, rightExpr) 
                    | BinaryOperator.SubtractChecked       -> Expression.SubtractChecked (leftExpr, rightExpr) 
                    | BinaryOperator.And                   -> Expression.Add (leftExpr, rightExpr) 
                    | BinaryOperator.Or                    -> Expression.Or (leftExpr, rightExpr) 
                    | BinaryOperator.ExclusiveOr           -> Expression.ExclusiveOr (leftExpr, rightExpr) 
                    | BinaryOperator.LeftShift             -> Expression.LeftShift (leftExpr, rightExpr) 
                    | BinaryOperator.RightShift            -> Expression.RightShift (leftExpr, rightExpr) 
                    | BinaryOperator.AndAlso               -> Expression.AndAlso (leftExpr, rightExpr) 
                    | BinaryOperator.OrElse                -> Expression.OrElse (leftExpr, rightExpr) 
                    | BinaryOperator.Equal                 -> Expression.Equal (leftExpr, rightExpr) 
                    | BinaryOperator.NotEqual              -> Expression.NotEqual (leftExpr, rightExpr) 
                    | BinaryOperator.GreaterThan           -> Expression.GreaterThan (leftExpr, rightExpr) 
                    | BinaryOperator.GreaterThanOrEqual    -> Expression.GreaterThanOrEqual (leftExpr, rightExpr) 
                    | BinaryOperator.LessThan              -> Expression.LessThan (leftExpr, rightExpr) 
                    | BinaryOperator.LessThanOrEqual       -> Expression.LessThanOrEqual (leftExpr, rightExpr) 
                    | BinaryOperator.Coalesce              -> Expression.Coalesce (leftExpr, rightExpr) 
                    | BinaryOperator.ArrayIndex            -> Expression.ArrayIndex (leftExpr, rightExpr) 
                    | _ -> failwithf "Binary operation %O not supported" op
                    :> Expression 

            | _ -> failwith "Excepting 2 arguments and none type argument"

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.Add)>]
    let Add (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.AddChecked)>]
    let AddChecked (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.Divide)>]
    let Divide (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.Modulo)>]
    let Modulo (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.Multiply)>]
    let Multiply (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.MultiplyChecked)>]
    let MultiplyChecked (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.Power)>]
    let Power (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.Subtract)>]
    let Subtract (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.SubtractChecked)>]
    let SubtractChecked (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.And)>]
    let And (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.Or)>]
    let Or (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.ExclusiveOr)>]
    let ExclusiveOr (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.LeftShift)>]
    let LeftShift (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.RightShift)>]
    let RightShift (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.AndAlso)>]
    let AndAlso (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.OrElse)>]
    let OrElse (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.Equal)>]
    let Equal (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.NotEqual)>]
    let NotEqual (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.GreaterThan)>]
    let GreaterThan (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.GreaterThanOrEqual)>]
    let GreaterThanOrEqual (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.LessThan)>]
    let LessThan (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.LessThanOrEqual)>]
    let LessThanOrEqual (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.Coalesce)>]
    let Coalesce (left : obj, right : obj) : bool = substitutionCall ()

    [<Extension>]
    [<BinaryOperatorSubstitution(BinaryOperator.ArrayIndex)>]
    let ArrayIndex (left : obj, right : obj) : bool = substitutionCall ()
